<template>
  <div ref="videoWrap" class="relative w-full h-full videoPlayer">
    <!-- 处理背景是否模糊 -->
    <div
      v-if="backgroundBlur && state.poster"
      class="backgroundBlur fullscreen z-1"
    >
      <div
        class="fullscreen cover"
        :style="`background-image: url('${state.poster}')`"
      >
        <div class="blur" />
      </div>
    </div>
    <video
      ref="videoRef"
      crossOrigin="anonymous"
      :data-poster="props.poster"
      playsinline="true"
      :preload="props.preload"
      class="video-js vjs-big-play-centered"
      :class="{
        lazy: props.lazy
      }"
      @click="toPause"
    />
    <!--      -->
    <!-- 自定义播放按钮 -->
    <div
      v-if="state.bigPlayButton"
      class="video-control-play cursor-pointer"
      @click="toPlay"
    >
      <img
        v-if="props.bigPlayButtonType === 'triangle'"
        class="w-full"
        src="@/assets/img/video_play_btn_triangle.png"
      />
      <img v-else class="w-full" src="@/assets/img/video_play_big.png" />
    </div>
    <!-- 自定义切换是否静音 -->
    <div
      v-if="!props.controls && state.volumePanel"
      class="video-control-volumePanel z-4 cursor-pointer"
      @click="onChangeMuted"
    >
      <img v-if="state.muted" src="@/assets/img/sound_muted.png" />
      <img v-else src="@/assets/img/sound.png" />
    </div>
    <!-- 自定义播放进度条 -->
    <template v-if="!props.controls && props.showProgress">
      <div
        class="w-full video-progress absolute -bottom-14px z-4 h-30px flex items-center hidden"
      >
        <div class="video-progress-control w-full">
          <div
            ref="progressRef"
            tabindex="0"
            class="video-progress-holder w-full"
            role="slider"
            aria-label="Progress Bar"
          >
            <div
              class="video-play-progress"
              aria-hidden="true"
              :style="{
                width: state.percent + '%',
                backgroundColor: props.progressColor || '#fff'
              }"
            >
              <!-- 定义是否可拖拽 -->
              <div
                v-if="props.allowDragger"
                class="video-slider-bar"
                :style="{
                  right: state.percent < 50 ? '-12px' : 0,
                  transform: state.isDropDown ? 'scale(2)' : 'unset'
                }"
                @mousedown="onMousedown"
                @touchstart="onTouchstart"
                @touchmove="onTouchmove"
                @touchend="onTouchend"
              />
            </div>
          </div>
        </div>
      </div>
    </template>
    <!-- 自定义是否显示时间 -->
    <!-- <div
      v-if="state.showMoveTime"
      class="video-time w-full h-78px flex items-center absolute bottom-2px  bg-opacity-50 z-2 text-[#fff]  text-20px justify-center"
    >
      {{ currentTimeDisplay }} /&nbsp;
      <span class="opacity-50"> {{ durationTimeDisplay }}</span>
    </div> -->
    <!-- 遮罩层处理点击屏幕 -->
    <div class="mask fullscreen z-3" @click="toPause" />
  </div>
</template>
<script setup>
import {
  computed,
  onMounted,
  ref,
  onUnmounted,
  nextTick,
  watch,
  reactive,
  toRefs
} from 'vue'
import { getVideoFileType, handerImageURL } from '@/utils/url.js'
import { watchEffect } from 'vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css'

const emit = defineEmits(['playing', 'dragging', 'ended', 'pause'])
const props = defineProps({
  sources: {
    type: [String, Array],
    default: ''
  },
  poster: {
    type: String,
    default: ''
  },
  src: {
    type: String,
    default: ''
  },
  autoplay: {
    type: Boolean,
    default: false
  },
  controls: {
    type: Boolean,
    default: false
  },
  preload: {
    type: String,
    default: 'auto'
  },
  lazy: {
    type: Boolean,
    default: true
  },
  muted: {
    // 静音
    type: Boolean,
    default: true
  },
  fluid: {
    // 静音
    type: Boolean,
    default: false
  },
  bigPlayButton: {
    type: Boolean,
    default: true
  },
  // circle triangle
  bigPlayButtonType: {
    type: String,
    default: 'circle'
  },
  volumePanel: {
    type: Boolean,
    default: false
  },
  loop: {
    type: Boolean,
    default: false
  },
  showProgress: {
    type: Boolean,
    default: false
  },
  backgroundBlur: {
    type: Boolean,
    default: false
  },
  progressColor: {
    type: String,
    default: ''
  },
  allowDragger: {
    type: Boolean,
    default: false
  },
  showMoveTime: {
    type: Boolean,
    default: false
  },
  observerGroup: {
    type: Boolean,
    default: false
  },
  threshold: {
    type: String,
    default: '0.3'
  }
})

//  video Dom
const videoRef = ref(null)
const progressRef = ref(null)
const videoWrap = ref(null)

const state = reactive({
  videoPlayer: null,
  // 是否正在移动进度条
  isMovingProgressBar: false,
  // 移动进度条时是否显示时间
  showMoveTime: false,
  bigPlayButton: false,
  // 切换muted按钮
  volumePanel: false,
  muted: props.muted,
  // 当前播放状态
  currentPlaying: false,
  duration: 0,
  currentTime: 0,
  percent: 0,
  currentTimeDisplay: 0,
  isDropDown: false,
  poster: handerImageURL(props.poster),
  videoObserver: null,
  isEnding: false
})
/**
 * 处理视频显示时间
 */
// const durationTimeDisplay = computed(() => {
//   const a = parseInt(state.duration / 60)
//   const b = parseInt(state.duration % 60)
//   return `${a < 10 ? '0' + a : a}:${b < 10 ? '0' + b : b}`
// })
// const currentTimeDisplay = computed(() => {
//   const a = parseInt(state.currentTime / 60)
//   const b = parseInt(state.currentTime % 60)
//   return `${a < 10 ? '0' + a : a}:${b < 10 ? '0' + b : b}`
// })

/**
 * 处理获取视频资源
 */
const videoSources = computed(() => {
  return props.src
    ? [{ src: props.src, type: getVideoFileType(props.src) }]
    : props.sources
})

const handleBgLazyLoad = item => {
  return new Promise(resolve => {
    const width = item.getAttribute('w')
    let url = item.getAttribute('data-poster')
    if (url === '' || url === null) {
      return
    }
    let quality = item.getAttribute('q') || 60

    let img = new Image()
    if (~url.indexOf('x-oss-process')) {
      img.src = url
    } else {
      img.src = handerImageURL(url, {
        q: quality,
        w: width
      })
    }
    img.onload = function () {
      item.setAttribute('poster', img.src)
      item.removeAttribute('data-poster')
      resolve()
    }
  })
}
const handlerInit = async () => {
  const el = videoWrap.value.querySelectorAll('video[data-poster]')[0]
  props.poster && handleBgLazyLoad(el)
  init()
}

const lazyVideo = () => {
  if ('IntersectionObserver' in window) {
    state.videoObserver = new IntersectionObserver(
      function (entries) {
        try {
          entries.forEach(video => {
            if (video.isIntersecting) {
              !props.observerGroup &&
                state.videoObserver?.unobserve?.(videoWrap.value)
              !state.videoPlayer && handlerInit()
              if (state.videoPlayer && props.autoplay) {
                toPlay()
              }
            } else {
              state.videoPlayer && toPause()
            }
          })
        } catch (err) {
          //
        }
      },
      {
        threshold: props.threshold
      }
    )
    state.videoObserver.observe(videoWrap.value)
  } else {
    init()
  }
}
/**
 * 计算视频播放的进度
 */
const calcVideoProgress = e => {
  const dom = e.target.childNodes[0]
  state.currentTime = dom.currentTime
  state.duration = dom.duration
  state.percent = (state.currentTime / state.duration) * 100
  if (Math.floor(dom.currentTime) === 0) {
    state.isEnding = false
  }
  if (
    Math.floor(dom.duration) - Math.floor(dom.currentTime) <= 1 &&
    !state.isEnding
  ) {
    state.isEnding = true
    emit('ended', e)
  }
}

/**
 * 设置当前视频的时间
 */
const setCurrentTime = () => {
  state.videoPlayer.currentTime((state.percent * state.duration) / 100)
}

// 记录点击的时候当前播放状态
let isPlaying = false
/**
 * 拖拽进度条
 */
const onTouchstart = () => {
  isPlaying = state.currentPlaying
  state.isDropDown = true
}
const onTouchmove = e => {
  state.isMovingProgressBar = true
  state.showMoveTime = props.showMoveTime
  const max = window.innerWidth
  const x = e.pageX || e.touches[0].pageX
  if (x <= max) {
    const width = progressRef.value.offsetWidth
    state.percent = (x * 100) / width
  } else {
    state.percent = 100
  }
  setCurrentTime()
}
const onTouchend = e => {
  state.isDropDown = false
  if (state.currentPlaying) {
    toPlay(e)
  } else {
    toPause()
  }
}
const onMousedown = () => {
  onTouchstart()
  document.onmousemove = onTouchmove
  document.onmouseup = () => {
    onTouchend()
    document.onmousemove = null
    document.onmouseup = null
  }
}
/**
 * 初始化
 */
const init = () => {
  state.videoPlayer = videojs(videoRef.value, {
    controls: props.controls, // 是否显示控制条
    preload: props.preload,
    // autoplay: props.autoplay,
    fluid: props.fluid, // 自适应宽高
    muted: props.muted, // 是否静音
    sources: videoSources.value,
    bigPlayButton: false,
    // 禁用画中画
    disablePictureInPicture: true,
    loop: props.loop,
    html5: { vhs: { overrideNative: true } }
  })
  state.videoPlayer.on('ready', function () {
    const videos = document.querySelectorAll('video')
    videos.forEach(video => {
      video.load()
    })
    if (props.autoplay) {
      state.videoPlayer.play()
    }
    state.volumePanel = props.volumePanel
    state.bigPlayButton = props.bigPlayButton
  })
  state.videoPlayer.on('loadeddata', function (e) {
    calcVideoProgress(e)
  })
  state.videoPlayer.on('timeupdate', function (e) {
    calcVideoProgress(e)
  })
  state.videoPlayer.on('seeked', function (e) {
    calcVideoProgress(e)
  })
  state.videoPlayer.on('play', function () {
    state.currentPlaying = true
  })
  state.videoPlayer.on('pause', function (e) {
    state.currentPlaying = false
    emit('pause', e)
  })
  // state.videoPlayer.on('ended', function (e) {
  //   emit("ended",e);
  // });
}

/**
 * 点击播放
 */
const toPlay = e => {
  e && e.stopPropagation()
  state.videoPlayer.play()
}
/**
 * 切换是否静音
 */
const onChangeMuted = () => {
  state.muted = !state.muted
  state.videoPlayer.muted(state.muted)
}
const toPause = () => {
  try {
    state.videoPlayer.pause()
  } catch (err) {
    //
  }
}

const reload = () => {
  toPause()
  state.videoPlayer?.src(props.src)
  state.videoPlayer?.load(props.src)
}
const getCurrentTime = () => state.currentTime
// 监听video src变化
watch(
  () => props.src,
  val => {
    console.log(val, 'src')
    reload()
  }
)
/**
 * 拖拽时监听按下时暂停视频
 */
watch(
  () => state.isDropDown,
  val => {
    if (val) {
      toPause()
    } else {
      state.showMoveTime = false
      state.isMovingProgressBar = false
      if (isPlaying) {
        toPlay()
      }
    }
  }
)
watch(
  () => state.isMovingProgressBar,
  val => {
    emit('dragging', {
      isMovingProgressBar: val
    })
  }
)

watchEffect(() => {
  state.currentTime > 0 &&
    emit('playing', {
      time: state.currentTime,
      progressPercent: state.percent
    })
})
/**
 * 拖拽时监听按下时暂停视频
 */
watch(
  () => state.isDropDown,
  val => {
    if (val) {
      toPause()
    } else {
      state.showMoveTime = false
      state.isMovingProgressBar = false
      if (isPlaying) {
        toPlay()
      }
    }
  }
)

onMounted(() => {
  nextTick(() => {
    if (props.lazy) {
      lazyVideo()
    } else {
      init()
    }
  })
})

onUnmounted(() => {
  if (state.videoPlayer) {
    state.videoPlayer.dispose()
  }
  videoWrap.value && state.videoObserver?.unobserve?.(videoWrap.value)
})
defineExpose({
  play: toPlay,
  pause: toPause,
  reload,
  getCurrentTime,
  isMovingProgressBar: toRefs(state).isMovingProgressBar
})
</script>
<style lang="scss" scoped>
.videoPlayer {
  :deep(.c-progress__percentage) {
    display: none !important;
  }

  .fullscreen {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

  .backgroundBlur {
    .cover {
      z-index: 0;
      display: flex;
      flex-flow: column;
      justify-content: center;
      align-items: center;
      overflow: hidden;
      background-position: center;
      background-size: cover;
      background-repeat: no-repeat;

      .blur {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.1);
        -webkit-backdrop-filter: blur(1rem);
        backdrop-filter: blur(1rem);
      }
    }
  }

  .video-js {
    width: 100%;
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    background-color: #f2f2f2;

    :deep(.vjs-picture-in-picture-control.vjs-disabled) {
      display: none;
    }

    :deep(.vjs-poster) {
      z-index: 1;
    }

    :deep(.vjs-loading-spinner) {
      z-index: 2;
    }

    :deep(video.vjs-tech) {
      z-index: 2;
    }

    :deep(.vjs-control-bar) {
      z-index: 4;
      background: linear-gradient(
        180deg,
        rgba(0, 0, 0, 0) 0%,
        rgba(0, 0, 0, 0.5) 100%
      );

      // .vjs-fullscreen-control {
      //   display: none;
      // }
    }
  }

  :deep(.vjs-has-started .vjs-control-bar),
  :deep(.vjs-audio-only-mode .vjs-control-bar) {
    opacity: 1 !important;
  }

  .video-control-play {
    position: absolute;
    z-index: 4;
    top: 50%;
    right: 50%;
    width: 64px;
    height: 64px;
    transform: translate(50%, -50%);
    cursor: pointer;
  }

  &.video-play-top-right .video-control-play {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 40px;
    height: 40px;
    transform: unset;
  }

  &.video-play-big .video-control-play {
    width: 72px;
    height: 72px;
  }

  :deep(.vjs-playing + .video-control-play) {
    display: none;
  }

  .vjs-paused + .video-control-play {
    display: block;
  }

  .video-control-volumePanel {
    position: absolute;
    bottom: 16px;
    right: 16px;
    display: flex;
    display: none;
    justify-content: center;
    align-items: center;
    width: 32px;
    height: 32px;
    border-radius: 20px;
    background: rgba(0, 0, 0, 1);

    img {
      width: 20px;
      height: 20px;
    }
  }

  &.video-volumn-bottom-left .video-control-volumePanel {
    bottom: 16px;
    left: 16px;
  }

  :deep(.vjs-has-started ~ .video-control-volumePanel) {
    display: flex;
  }

  :deep(.vjs-has-started ~ .video-progress, .video-progress:hover) {
    display: flex;
  }

  .video-progress-control {
    display: flex;
    align-items: center;
    background-color: rgba(0, 0, 0, 0.3);
    touch-action: none;

    .video-progress-holder {
      flex: auto;
      height: 2px;
      transition: all 0.2s;
    }

    .video-progress-holder .video-play-progress {
      position: absolute;
      display: block;
      width: 0;
      height: 100%;
      height: 2px;
      padding: 0;
      margin: 0;
    }

    .video-play-progress {
      position: relative;
      background-color: #fff;
    }

    .video-play-progress .video-slider-bar {
      position: absolute;
      z-index: 3;
      top: -4px;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: #fff;
      cursor: pointer;
    }
  }

  @media screen and (max-width: 768px) {
    .video-control-volumePanel {
      border: 1px solid rgba(255, 255, 255, 0.4);
      border-radius: 40px;

      img {
        width: 20px;
        height: 20px;
      }
    }
  }
}
</style>
